package cn.iocoder.yudao.module.data.core;

import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.ReUtil;
import cn.hutool.core.util.StrUtil;
import com.google.common.collect.Sets;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;

import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.concurrent.CompletableFuture;
import java.util.function.Function;
import java.util.regex.MatchResult;
import java.util.stream.Collectors;

/**
 * <p>
 * 正则
 * </p>
 *
 * @author pokbrin
 * @date 2023-07-20 16:33
 */
@Slf4j
public class StockPattern {
    /**
     * 沪市A股
     * 以600、601、603或605打头
     */
    public final static String SH_A_REG = "600\\d{3}|601\\d{3}|603\\d{3}|605\\d{3}";
    /**
     * 沪市B股
     * 以900打头
     */
    public final static String SH_B_REG = "900\\d{3}";
    /**
     * 沪市科创
     */
    public final static String SH_KC_REG = "688\\d{3}";

    /**
     * 深市A股
     */
    public final static String SZ_A_REG = "000\\d{3}|002\\d{3}";
    /**
     * 深市B股
     */
    public final static String SZ_B_REG = "200\\d{3}";
    /**
     * 深市创业板
     */
    public final static String SZ_CY_REG = "300\\d{3}";


    public final static Set<StockType> REGS;

    static {
        REGS = Sets.newHashSet(StockType.values());
        REGS.remove(StockType.NONE);
    }


    public static MatherResult match(String code, boolean throwEx) {
        MatherResult matherResult = REGS.stream().filter(StockType::exist)
                .map(stockType -> {
                    MatchResult matchResult = ReUtil.indexOf(stockType.getRegex(), code);
                    if (matchResult == null) {
                        return null;
                    }
                    String group = matchResult.group();
                    if (log.isDebugEnabled()) {
                        log.debug("### stock preCode[{}] match => [{}] use regex [{}-{}]",
                                code, group, stockType.getDesc(), stockType.getRegex());
                    }
                    return MatherResult.builder()
                            .preCode(code)
                            .afterCode(group)
                            .stockType(stockType)
                            .build();
                }).filter(Objects::nonNull)
                .findFirst().orElseGet(() -> MatherResult.builder().preCode(code).afterCode(code).stockType(StockType.NONE).build());
        if (throwEx && !matherResult.stockType.exist()) {
            throw new RuntimeException(String.format("code:[%s] not match", code));
        }
        return matherResult;
    }

    public static MatherResult match(String code) {
        return match(code, true);
    }

    public static Map<String, MatherResult> multiMatch(Set<String> codes) {
        List<CompletableFuture<MatherResult>> futures = codes.stream().map(code -> CompletableFuture.supplyAsync(() -> match(code)))
                .collect(Collectors.toList());
        return futures.stream().map(CompletableFuture::join)
                .filter(t -> t.getStockType().exist())
                .collect(Collectors.toMap(MatherResult::getPreCode, Function.identity(), (k1, k2) -> k2));
    }


    @Getter
    @Setter
    @Builder
    public static class MatherResult {

        private StockType stockType;
        /**
         * 原始code
         */
        private String preCode;
        /**
         * 转换后 6位数字
         */
        private String afterCode;

        @Override
        public String toString() {
            return StrUtil.format("[pre={}]-[after={}]-[{}]",
                    preCode, afterCode, ObjectUtil.defaultIfNull(this.stockType, StockType.NONE).getDesc());
        }
    }


    @Getter
    @AllArgsConstructor
    public static enum StockType {
        SH_A(SH_A_REG, "沪市A股"),
        SH_B(SH_B_REG, "沪市B股"),
        SH_KC(SH_KC_REG, "科创板"),
        SZ_A(SZ_A_REG, "深市A股票"),
        SZ_B(SZ_B_REG, "深市B股票"),
        SZ_CY(SZ_CY_REG, "创业板"),
        NONE("", "其他");

        public final static Set<StockType> SHS = Sets.newHashSet(SH_A, SH_B, SH_KC);
        public final static Set<StockType> SZS = Sets.newHashSet(SZ_A, SZ_B, SZ_CY);
        private final String regex;
        private final String desc;

        public boolean exist() {
            return this != NONE;
        }
    }

}
